﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

/* This class holds and manipulates the data for 3D camera transformations
 * View and Perspective
 * 
 */

namespace VuzixSample.model
{
    class Camera
    {
        private
            Vector3 g_cameraPosition;
            
            Vector3 g_ViewVector    = Vector3.Forward;
            Vector3 g_UpVector      = Vector3.Up;
            Vector3 g_LeftVector    = Vector3.Left;

            //stereoscopy
            float g_EyeSeparation = 3.0f;// Intraocular Distance: aka, Distance between left and right cameras.
            float g_FocalLength = 150.0f;// Screen projection plane: aka, focal length(distance to front of virtual screen).

            float fovy = 45.0f; //field of view in y-axis
            float nearZ = 1.0f; //near clipping plane
            float farZ = 10000.0f; //far clipping plane
            float aspect = 16 / 9;
            float frustumshift = 0;

            float right = 0.0f;
            float left = 0.0f;
            float top = 0.0f;

            Vector3 vLookatPt;
            Vector3 vStereoAdj;
            
            Eyes _currentEye;

        public Camera()
        {
            float   g_CameraXPosition = -30.0f; //30.0f
            float   g_CameraYPosition = -50.0f; //50.0f
            float   g_CameraZPosition = 100.0f; //-100.0f
            
            g_cameraPosition = new Vector3(g_CameraXPosition, g_CameraYPosition, g_CameraZPosition);
        }
        public void Initialise(Viewport viewport)
        {
            Initialise(viewport, true);
        }
        public void Initialise(Viewport viewport, bool isStereo)
        {
            //sets top of frustum based on fovy and near clipping plane
            top = nearZ * (float)Math.Tan(MathHelper.ToRadians(fovy) / 2);
            aspect = (float)(viewport.Width) / (float)(viewport.Height);//screen aspect ratio
            if (isStereo)
                aspect = aspect * 2;

            //sets right of frustum based on aspect ratio
            right = aspect * top;
            frustumshift = (g_EyeSeparation / 2) * nearZ / g_FocalLength;

            //adjust vectors
            _currentEye = Eyes.LEFT_EYE;
        }
        public float x
        {
            get { return g_cameraPosition.X;  }
            set { g_cameraPosition.X = value; }
        }
         public float y
        {
            get { return g_cameraPosition.Y;  }
            set { g_cameraPosition.Y = value; }
        }
         public float z
        {
            get { return g_cameraPosition.Z;  }
            set { g_cameraPosition.Z = value; }
        }
        public void Rotate(Matrix matOrient)
        {
            g_ViewVector    = Vector3.Forward;
            g_UpVector      = Vector3.Up;
            g_LeftVector    = Vector3.Left;

            g_ViewVector = Vector3.Transform(g_ViewVector, matOrient);
            g_LeftVector = Vector3.Transform(g_LeftVector, matOrient);
            g_UpVector = Vector3.Transform(g_UpVector, matOrient);
        }   

        public Eyes CurrentEye
        {
            get{ return _currentEye; }
            set{ 
                _currentEye = value;

                vLookatPt = g_cameraPosition + g_ViewVector;

                // set proper fields for left/right or mono viewing.
                switch (_currentEye)
                {
                    case Eyes.LEFT_EYE:
                        left = -right + frustumshift;
                        right = right + frustumshift;
                        // Vector adjust:
                        vStereoAdj = g_LeftVector * g_EyeSeparation;
                        g_cameraPosition += vStereoAdj;
                        vLookatPt += vStereoAdj;
                        break;
                    case Eyes.RIGHT_EYE:
                        left = -right - frustumshift;
                        right = right - frustumshift;
                        // Vector adjust:
                        vStereoAdj = g_LeftVector * g_EyeSeparation;
                        g_cameraPosition -= vStereoAdj;
                        vLookatPt -= vStereoAdj;
                        break;
                    case Eyes.MONO_EYES:
                        left = -right;
                        break;
                }
            }
        }
        public Matrix Projection
        {
            get
            {
                return Matrix.CreatePerspectiveOffCenter(left, right, -top, top, nearZ, farZ);
            }
        }
        public Matrix View
        {
            get
            {
                return Matrix.CreateLookAt(g_cameraPosition, vLookatPt, g_UpVector);
            }
        }

        public void Dispose()
        {

        }
    }
}
